package oop.ex2.tools;

import java.util.regex.*;

import oop.ex2.main.Block;
import oop.ex2.main.Sjavac;
import oop.ex2.main.UnknownCallingException;
import oop.ex2.main.UnknownStatementException;
import oop.ex2.main.Variable;
import oop.ex2.main.compilationException;

/**
 * 
 * @author yossavi
 *
 */
public class CheckType {
	
	/**
	 * 
	 * @author yossavi
	 *
	 */
	public enum VariableType  {
		INT("-?[0-9]+","int","1"), 
		DOUBLE("-?([0-9])+((\\.)([0-9])+)?","double","1.1"), 
		STRING("\".*\"","String","\"abc\""), 
		BOOLEAN("true|false|"+DOUBLE.getRegex(),"boolean","true"), 
		CHAR("'.'","char","'a'");
		
		private String regex;
		private String name;
		private String example;
		
		VariableType (String regex, String name, String example) {
			this.regex=regex;
			this.name=name;
			this.example=example;
		}
		
		public String getRegex() {
		       return this.regex;
		}
		public String getName() {
		       return this.name;
		}
		public String getExmple() {
		       return this.example;
		}

	}
	
	public static final String FINAL="final";
	public static final String VOID="void";
	public static final String RETURN="return";
	public static final String IF="if";
	public static final String WHILE="while";
	public static Block currentMethod;
	
	/**
	 * checks the text
	 * returns true if the text is valid variable name
	 * @param text- to check
	 * @return true if the text is valid variable name, false otherwise
	 */
	public static boolean isValidVariableName (String text) {
		return text.matches("(^[a-zA-Z]\\w*)|(^_\\w+)");
	}
	
	/**
	 * checks the text
	 * returns true if the text is valid method name
	 * @param text- to check
	 * @return true if the text is valid method name, false otherwise
	 */
	public static boolean isValidMethodName (String text) {
		return text.matches("(^[a-zA-Z]\\w*)");
	}
	
	/**
	 * checks the text
	 * returns true if the text is a command line
	 * ends with ";"
	 * @param text- to check
	 * @return true if the text is command, false otherwise
	 */
	public static boolean isCommand (String text) {
		return text.matches(".*;$");
	}
	
	/**
	 * checks the text
	 * returns true if the text is beginning of a block
	 * ends with "{"
	 * @param text- to check
	 * @return true if the text is beginning of a block, false otherwise
	 */
	public static boolean isBlock (String text) {
		return text.matches(".*\\{$");
	}
	
	/**
	 * checks the text
	 * returns true if the text is empty
	 * contains only spaces or tabs
	 * @param text
	 * @return
	 */
	public static boolean isEmpty (String text) {
		return text.matches("[ \\t]*");
	}
	
	/**
	 * gets type of variable and return true if it's valid type
	 * @param type- the type to check
	 * @return true if it's valid type else otherwise
	 */
	public static boolean isVariableType (String type) {
		boolean isValidType=false;
		// run over enums
		for (VariableType v: VariableType.values()) {
			if (v.getName().equals(type))
				isValidType=true;
		}
		return isValidType;
	}
	
	/**
	 * gets type of method and return true if it's valid type
	 * @param type- the type to check
	 * @return true if it's valid type else otherwise
	 */
	public static boolean isMethodType (String type) {
		boolean isValidType=isVariableType(type);
		if (VOID.equals(type))
			isValidType=true;
		
		return isValidType;
	}
	
	/**
	 * get string that contains call to a method
	 * check correctness of calling and return is type
	 * @param method - the method to check
	 * @return the type of the method
	 * @throws compilationException
	 */
	public static String getmethodType (String method) throws compilationException {
		Pattern pattern = Pattern.compile("([a-zA-Z]\\w*)\\((.*)\\)");
		Matcher matcher = pattern.matcher(method);
		
		if (matcher.find()) {
			for (Block m: Sjavac.methods) {
				if (m.getName().equals(matcher.group(1))) {
					m.checkParamCorrectness (matcher.group(2));
					return m.getType();
				}
			}
			throw new UnknownCallingException("Method dosen't exist");
		}
		throw new UnknownStatementException("UnKnown command");
	}
	
	/**
	 * get some value and some type and return if the value's type and the type compatible
	 * @param value- the value to check is type
	 * @param type- the type to compatible
	 * @return true if it's compatible, false otherwise
	 * @throws compilationException
	 */
	public static boolean isValueMuchType (String value, String type) throws compilationException {
		// get the current method call was made from
		Block method=CheckType.currentMethod;
		VariableType varType = VariableType.valueOf(type.toUpperCase());
		// if it isn't primitive type
		if (!value.matches(varType.getRegex())) {
			// run over local variables
			if (method!=null) {
				for (Variable v: method.variables) {
					if (v.getName().equals(value))
						return(v.getType().equals(type) && v.getValue());
				}				
			}
			// run over members
			for (Variable v: Sjavac.members) {
				if (v.getName().equals(value)) {
					return(v.getType().equals(type) && v.getValue());
				}
			}
			// get the first word in the value
			TextProcessor processedLine = new TextProcessor(value);
			String nextWord=processedLine.findNextWord();
			// check if it's a method and with compatible return type
			if(isExistMethod(nextWord) && getmethodType(value).equals(type))
				return true;	
			return false;
		}
		return true;
		
	}
	
	/**
	 * return if the method exist
	 * @param name- of the method to check
	 * @return true if it's exist, false otherwise
	 */
	public static boolean isExistMethod (String name) {
		boolean isExist=false;
		for (Block m: Sjavac.methods) {
			if (m.getName().equals(name))
				isExist=true;
		}
		return isExist;
	}
	
	/**
	 * return if the member exist
	 * @param name- of the variable to check
	 * @return true if it's exist, false otherwise
	 */
	public static boolean isExistMember (String name) {
		boolean isExist=false;
		for (Variable v: Sjavac.members) {
			if (v.getName().equals(name))
				isExist=true;
		}
		return isExist;
	}
}
